package org.mashart.pink.m.store;

import java.io.OutputStream;
import java.io.IOException;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.InvalidRecordIDException;
import javax.microedition.rms.RecordEnumeration;
import org.mashart.pink.m.ws.WsConnector;

/**
 * Manager of the persistent storage for the waypoints on mobile device.
 * It can store the measured waypoints, if the internet connection is unavailable;
 *  and upload the stored waypoints later, when the internet is available.
 * This class and its methods are not synchronized, concurrent invocations may lead to non-determinate effects.
 */
public class RecordManager {
    private static RecordManager unique;
    private final String recordStoreName = "waypoits";
    private RecordStore recordStore;
    private boolean isOpened = false;

    /**
     * Factory method
     * @return a unique instance of <code>RecordManager</code>.
     */
    public static synchronized RecordManager getInstance(){
        if(unique == null){
            unique = new RecordManager();
        }
        return unique;
    }

    /**
     * Check if the record store already exists.
     */
    public boolean recordStoreExists(){
        // list the record stores
        String[] names = RecordStore.listRecordStores();
        // no record store
        if(names == null){
            return false;
        }
        // search for the record store that we use
        for(int i = 0; i < names.length; i++){
            if(names[i].equals(recordStoreName)){
                return true;
            }
        }
        return false;
    }

    /**
     * Get the number of records.
     * If the record store does not exist, return 0.
     */
    public int getNumRecords(){
        int n = 0;

        if( recordStoreExists() ){
            // open the record store
            open();
            try{
                n = recordStore.getNumRecords();
            }catch(RecordStoreException rse){
                rse.printStackTrace();
            }
            // close the record store
            close();
        }
        return n;
    }

    /**
     * Open the record store. If the record store does not exist, create one.
     * If the record store is already opened, do nothing. So the record store can only be opened once.
     */
    private int open(){
        if(!isOpened){
            try{
                recordStore = RecordStore.openRecordStore(recordStoreName, true);
            }catch(RecordStoreException rse){
                rse.printStackTrace();
                return -1;
            }
            isOpened = true;
        }
        return 1;
    }

    /**
     * Insert a record
     */
    public int insert(byte[]b){
        open();
        try{
            recordStore.addRecord(b, 0, b.length);
        }catch(RecordStoreException rse){
            rse.printStackTrace();
            return -1;
        }
        close();
        return 1;
    }

    /**
     * Send all the records.
     * If all records are sent successfully, delete the record store.
     * If failed in sending any one record, delete the sent records and keep the unsent records.
     * The action will also be send.
     * @param ws_client web service connector, should be initilazed before calling this method.
     */
    public int send(WsConnector ws_client){
        RecordEnumeration re;
        // maximum and minimum id of the sent records
        int maxId = Integer.MIN_VALUE;
        int minId = Integer.MAX_VALUE;
        // result of sending
        int res = -1;
        // recordId
        int id;

        try{
            open();
            // The enumeration order is tested to be correct under Symbian 60 V3, Nokia N95.
            // If reverse order is used in other platform, it is necessary to write a RecordComparator to ensure the enumeration order.
            re = recordStore.enumerateRecords(null, null, false);
            while(re.hasNextElement()){
                id = re.nextRecordId();
                byte[] b = recordStore.getRecord(id);
//                byte[] b = re.nextRecord();
                String s = new String(b);

//                System.out.println("sendAll sending id=" + id);

                // if the String is an action
                if( s.startsWith("action=") ){
                    // get the action name
                    String action = s.substring("action=".length());
                    // pushAction
                    res = ws_client.pushAction(action);
                }else{
                    res = ws_client.push(s);
                }
                
                if( res > 0 ){
                    if( maxId < id ){
                        maxId = id;
                    }
                    if( minId > id ){
                        minId = id;
                    }
                    System.out.println("maxId=" + maxId + " minId=" + minId);
                }else{
//                    System.out.println("break! maxId=" + maxId + " minId=" + minId);
                    // if error occured when sending a record, then jump out
                    break;
                }
             }
            close();

            // if all records are sent successfully, delete the record store
            if( res > 0 ){
                System.out.println("numRecords: " + getNumRecords());
                System.out.println("deleting all records");
                delete();
            }
            // otherwise, only delete the sent records
            else{
                open();
                for( int i = minId ; i <= maxId ; i++ ){
                    try{
                        System.out.println("deleting id=" + i);
                        recordStore.deleteRecord(i);
                    }catch( InvalidRecordIDException rse ){
                        // just skip the invalid id, should not happen
                    }
                }
                close();
            }
        }catch(RecordStoreException rse){
            rse.printStackTrace();
            res = -1;
        }
        return res;
    }

    /**
     * Send out all the records.
     * @param ws_client web service connector, should be initilazed before calling this method.
     */
    public int writeOutputStream(OutputStream ostream){
        RecordEnumeration re;
        open();
        try{
            re = recordStore.enumerateRecords(null, null, false);
            while(re.hasNextElement()){
                byte[] b = re.nextRecord();
                // write the bytes into the output stream
                try{
                    ostream.write(b);
                    ostream.write('\n');
                }catch(IOException e){
                    e.printStackTrace();
                    return -1;
                }
            }
        }catch(RecordStoreException rse){
            rse.printStackTrace();
            return -1;
        }

        close();
        return 1;
    }

    /**
     * Close the record store.
     */
    private int close(){
        if(isOpened){
            try{
                recordStore.closeRecordStore();
            }catch(RecordStoreException rse){
                rse.printStackTrace();
                return -1;
            }
            isOpened = false;
        }
        return 1;
    }

    /**
     * Delete the record store.
     * If the record store is open, it must be closed first. If it is opened twice, it must be closed twice.
     */
    public int delete(){
        close();
        if( recordStoreExists() ){
            try{
                RecordStore.deleteRecordStore(recordStoreName);
            }catch(RecordStoreException rse){
                rse.printStackTrace();
                return -1;
            }
        }
        return 1;
    }
}
